<template>
  <div>
    <!-- 3. 告诉适配组件，我是按照多少宽高设计图实现的 -->
    <VScaleScreen width="1920" height="1080">
      <!-- <img src="@/assets/img.png" alt=""> -->
      <!-- 2D -->
      <div class="all-charts">
        <!-- 园区概况 -->
        <div class="section-one">
          <img
            class="img-header"
            src="https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/smartPark/%E5%A4%A7%E5%B1%8F%E5%88%87%E5%9B%BE/%E5%9B%AD%E5%8C%BA%E6%A6%82%E5%86%B5%402x.png"
            alt=""
          />
          <div class="icons-container">
            <div class="item">
              <div class="icons-item building-icon">
                <span class="number">
                  {{ parkInfo.base?.buildingTotal }}
                </span>
              </div>
              <span class="title">楼宇总数</span>
              <span class="unity">（栋）</span>
            </div>
            <div class="item">
              <div class="icons-item enterprise-icon">
                <span class="number">
                  {{ parkInfo.base?.enterpriseTotal }}
                </span>
              </div>
              <span class="title">入驻企业总数</span>
              <span class="unity">（家）</span>
            </div>
            <div class="item">
              <div class="icons-item car-icon">
                <span class="number">
                  {{ parkInfo.base?.parkingTotal }}
                </span>
              </div>
              <span class="title">车位总数</span>
              <span class="unity">（个）</span>
            </div>
            <div class="item">
              <div class="icons-item rod-icon">
                <span class="number">
                  {{ parkInfo.base?.chargePoleTotal }}
                </span>
              </div>
              <span class="title">一体杆总数</span>
              <span class="unity">（个）</span>
            </div>
          </div>
        </div>
        <!-- 园区年度收入分析 -->
        <div class="section-two">
          <img
            class="img-header"
            src="https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/smartPark/%E5%A4%A7%E5%B1%8F%E5%88%87%E5%9B%BE/%E5%9B%AD%E5%8C%BA%E5%B9%B4%E5%BA%A6%E6%94%B6%E5%85%A5%E5%88%86%E6%9E%90%402x.png"
            alt=""
          />
          <div class="bar-chart-titile">
            <span>单位：元</span>
            <div>
              <span class="bar-icon blue-bar-icon"></span>
              <span class="bar-icon red-bar-icon"></span>
              收入情况
            </div>
          </div>
          <div class="bar-chart" ref="barChart"></div>
        </div>
        <!-- 园区产业分布 -->
        <div class="section-three">
          <img
            class="img-header"
            src="https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/smartPark/%E5%A4%A7%E5%B1%8F%E5%88%87%E5%9B%BE/%E5%9B%AD%E5%8C%BA%E4%BA%A7%E4%B8%9A%E5%88%86%E5%B8%83%402x.png"
            alt=""
          />
          <div class="pie-chart" ref="pieChart"></div>
        </div>
      </div>
      <!-- 3D -->
      <div class="model-container">
        <div class="canvas-3d"></div>
      </div>
    </VScaleScreen>
  </div>
  <!-- 加载建筑说明标签 -->
  <build-info-vue></build-info-vue>
  <!-- 汽车说明标签 -->
  <car-info-vue></car-info-vue>
</template>

<script setup>
// 目标：缩放适配
// 1. 下载 v-scale-screen 插件
// 2. 引入插件暴露的组件使用

// 目标：同步 token
// 因为：主项目带登录流程，保存 token 在 localhost 域名下的浏览器本地中，子项目大屏幕也要从 localhost 访问获取到主项目保存的 token
// URL 格式：域名:端口号/路径?查询参数
// 域名：决定你访问哪台电脑/服务器 （localhost 本机电脑）
// 端口号：决定你访问哪个服务程序
// 路径：你要访问程序下的哪个资源/路由页面
import VScaleScreen from "v-scale-screen";
// import * as echarts from "echarts";
import { useParkInfo } from "./use/useParkInfo.js";
import { useBarChart } from "./use/useBarChart.js";
import { usePieChart } from "./use/usePieChart.js";

const { parkInfo, getParkInfoFn } = useParkInfo();
const { barChart, initBarChart } = useBarChart();
const { pieChart, initPieChart } = usePieChart();
// 园区概况
const init = async () => {
  // 调用给状态赋值的动作
  await getParkInfoFn();

  // 收入分析
  initBarChart(parkInfo);
  // 产业分布
  initPieChart(parkInfo);
};
init();

// import { useWindowScroll } from '@vueuse/core'

// // 调用组合函数，得到一个 x 水平方向滚动距离的响应式对象，y 垂直方向响应式对象
// const { x, y } = useWindowScroll()

/******************************************************** */
// 3D
import { Create3d } from "@/three3d";
import * as THREE from "three";
import { LoadingManager } from "@/utils/LoadingManager.js";
import { Car } from "@/three3d/Model/Car.js";
// 鼠标管理类
import { MouseHandler } from "@/utils/MouseHandler.js";
// 建筑信息标签组件
import BuildInfoVue from "@/components/build-info.vue";
import { CSS2DObject } from "three/examples/jsm/renderers/CSS2DRenderer";
import { getBuildInfoAPI } from "@/apis/index.js";
import { useStore } from "vuex";
import { CameraBuildPositon, CameraParams } from "@/const/index.js";
import gsap from "gsap";
// 车辆信息标签组件
import CarInfoVue from "@/components/car-info.vue";
// import { getPoints } from "@/utils/points.js";
import { EffectManager } from "@/utils/EffectManager.js";
const store = useStore();

import { cameraMove } from "@/utils/helper.js";
import { getCookie } from "@/utils/cookie.js";
// 抬杆/落杆方法
function liftingRod(scene, carDataObj, isOpen) {
  return new Promise((resolve) => {
    // 根据车辆信息，找到对应一体杆
    let statusObj = {
      0: "出场",
      1: "进场",
    };
    let poleAngle = {
      0: {
        // 出场杆子
        open: Math.PI / 2,
        close: 0,
      },
      1: {
        // 进场杆子
        open: -Math.PI / 2,
        close: 0,
      },
    };
    const pole = scene.getObjectByName(
      `${carDataObj.areaName}_${statusObj[carDataObj.car.status]}一体杆`
    );
    gsap.to(pole.rotation, {
      z: poleAngle[carDataObj.car.status][isOpen ? "open" : "close"],
      duration: 2,
      ease: "none", // 匀速
      onComplete: () => {
        // 杆子已经抬起，执行汽车效果
        resolve();
      },
    });
  });
}

setTimeout(async () => {
  // 初始化 3d 基础环境
  const { scene, camera, renderer, controls, dom, css2dRenderer } =
    new Create3d(".canvas-3d");
  // 初始化鼠标管理类（内部就在 dom 身上注册了鼠标移动和点击的事件监听）
  MouseHandler.getInstance().init(scene, camera, dom);
  // 保存车场里现有的所有车辆的实例对象
  const carList = [];

  // 加载背景图
  const cubeTextureLoader = new THREE.CubeTextureLoader();
  const envMap = cubeTextureLoader.load([
    new URL("@/assets/sky/1.jpg", import.meta.url).href,
    new URL("@/assets/sky/2.jpg", import.meta.url).href,
    new URL("@/assets/sky/3.jpg", import.meta.url).href,
    new URL("@/assets/sky/4.jpg", import.meta.url).href,
    new URL("@/assets/sky/5.jpg", import.meta.url).href,
    new URL("@/assets/sky/6.jpg", import.meta.url).href,
  ]);
  scene.background = envMap;

  // 把模型文件->加载成模型数据对象
  const modelList = await LoadingManager([
    new URL("@/assets/glb/park.glb", import.meta.url).href,
    new URL("@/assets/glb/car001.glb", import.meta.url).href,
    new URL("@/assets/glb/car002.glb", import.meta.url).href,
    new URL("@/assets/glb/car003.glb", import.meta.url).href,
    new URL("@/assets/glb/car004.glb", import.meta.url).href,
    new URL("@/assets/glb/car005.glb", import.meta.url).href,
    new URL("@/assets/glb/car006.glb", import.meta.url).href,
    new URL("@/assets/glb/car007.glb", import.meta.url).href,
  ]);
  // 第一个园区对象加载到场景中
  const park3d = modelList[0];
  scene.add(park3d);
  const carModelList = modelList.slice(1);

  // 车辆说明标签物体准备
  const carInfoDiv = document.querySelector(".car-info");
  carInfoDiv.style.pointerEvents = "all";
  const carInfo2d = new CSS2DObject(carInfoDiv);
  carInfo2d.visible = false;
  scene.add(carInfo2d);
  carInfoDiv.addEventListener("click", () => {
    carInfo2d.visible = false;
    // 把视角恢复到最起始位置
    cameraMove(
      camera,
      controls,
      CameraParams.initPos,
      new THREE.Vector3(...Object.values(CameraParams.initControlsTarget))
    );
  });

  // // 添加车辆到场景中
  // const initObj = {
  //   type: "PARKING_LIST",
  //   list: [
  //     {
  //       areaId: 4,
  //       areaName: "停车场1号", // 停车场区域名字
  //       car: {
  //         carNumber: "赣H11J9F",
  //         carTypeName: "小型车",
  //         chargeType: "temp",
  //         chargeTypeName: "临时停车",
  //         driverName: "莫苛",
  //         entranceTime: "2023-12-09 15:56:11",
  //         modelIndex: 3, // 模型下标
  //         parkingTime: "48分钟",
  //         status: 2, // 2 已在场，1 待进场，0 待出场
  //       },
  //       id: 31, // 车辆 id
  //       parkNum: "停车位1号", // 车辆所占车位名
  //       parkNumber: 1, // 车辆所占车位数字
  //     },
  //     {
  //       areaId: 4,
  //       areaName: "停车场1号", // 停车场区域名字
  //       car: {
  //         carNumber: "赣H11J9F",
  //         carTypeName: "小型车",
  //         chargeType: "temp",
  //         chargeTypeName: "临时停车",
  //         driverName: "莫苛",
  //         entranceTime: "2023-12-09 15:56:11",
  //         modelIndex: 3, // 模型下标
  //         parkingTime: "48分钟",
  //         status: 2, // 2 已在场，1 待进场，0 待出场
  //       },
  //       id: 31, // 车辆 id
  //       parkNum: "停车位5号", // 车辆所占车位名
  //       parkNumber: 5, // 车辆所占车位数字
  //     },
  //     {
  //       areaId: 4,
  //       areaName: "停车场1号",
  //       car: {
  //         carNumber: "甘H80106",
  //         carTypeName: "小型车",
  //         chargeType: "temp",
  //         chargeTypeName: "临时停车",
  //         driverName: "陈督丙",
  //         entranceTime: "2023-12-09 15:56:11",
  //         modelIndex: 1,
  //         parkingTime: "48分钟",
  //         status: 2,
  //       },
  //       id: 32,
  //       parkNum: "停车位6号",
  //       parkNumber: 6,
  //     },
  //     {
  //       areaId: 5,
  //       areaName: "停车场2号",
  //       car: {
  //         carNumber: "浙B93871",
  //         carTypeName: "小型车",
  //         chargeType: "temp",
  //         chargeTypeName: "临时停车",
  //         driverName: "岑迢",
  //         entranceTime: "2023-12-09 15:56:11",
  //         modelIndex: 5,
  //         parkingTime: "48分钟",
  //         status: 2,
  //       },
  //       id: 33,
  //       parkNum: "停车位9号",
  //       parkNumber: 9,
  //     },
  //   ],
  // };
  // // 遍历数据->根据数据创建效果和自己封装的车辆对象
  // initObj.list.forEach((dataObj) => {
  //   // 问题：如果多个车用同一个车辆模型对象，会共同一个
  //   // 解决：物体是可以调用 clone 方法克隆一个完全的一致的物体对象出来
  //   const car = new Car(
  //     scene,
  //     camera,
  //     controls,
  //     modelList[dataObj.car.modelIndex].clone(),
  //     dataObj,
  //     carInfo2d
  //   );

  //   carList.push(car);
  // });

  // 空白缓冲几何体图形
  const hoverGeometry = new THREE.BufferGeometry();
  const hoverMaterial = new THREE.MeshBasicMaterial({
    color: 0x1dd6ff,
    transparent: true, // 支持材质的透明度设置
    opacity: 0.5, // 透明度
  });
  const hoverMesh = new THREE.Mesh(hoverGeometry, hoverMaterial);
  hoverMesh.visible = false;
  scene.add(hoverMesh);

  // 获取 div 标签物体
  const buildInfoDiv = document.querySelector(".build-info");
  // div 标签 => 2D 物体
  const buildDiv2d = new CSS2DObject(buildInfoDiv);
  buildDiv2d.visible = false;
  // 加载到场景中
  scene.add(buildDiv2d);
  // 目标：点击 x 隐藏说明标签
  // x 绑定点击事件
  const buildInfoCloseImg = document.querySelector(".build-info .close-icon");
  buildInfoCloseImg.style.pointerEvents = "all"; // 单独让 x 可以让鼠标触摸到
  buildInfoCloseImg.addEventListener("click", () => {
    buildDiv2d.visible = false;
    // 把视角恢复到最起始位置
    cameraMove(
      camera,
      controls,
      CameraParams.initPos,
      new THREE.Vector3(...Object.values(CameraParams.initControlsTarget))
    );
    controls.enabled = true;
  });
  // 防止点击 x 让 canvas-3d 标签的按下事件触发，导致关闭说明标签又去到了另一个楼的点击效果
  buildInfoCloseImg.addEventListener("mousedown", (e) => e.stopPropagation());

  // 经验：找到一个 3D 物体，通过名字查找
  // 语法：父级物体对象.getObjectByName(物体名字) => 原地值：物体对象 （找一个）
  // 语法：物体对象.traverse(obj3d => {}) => 遍历指定物体中所有子物体对象
  park3d.traverse((obj3d) => {
    // 筛选我想要的物体（物体名字上规律）
    if (/(路线)$/.test(obj3d.name)) {
      // 隐藏路线物体：不耽误以后获取这个物体对象，坐标的计算
      obj3d.visible = false;
    } else if (/^办公楼\d+栋$/.test(obj3d.name)) {
      // 进到这里 obj3d 就是办公楼物体

      // 办公楼鼠标移动事件
      // 给办公楼订阅鼠标交互的消息，把物体和回调函数传入（内部鼠标检测到你滑过了办公楼，回调函数就会被触发
      MouseHandler.getInstance().addHoverMesh(obj3d, (target) => {
        // target 等价与 obj3d
        // console.log(target);
        // 目标：把楼宇几何图形和位置与旋转角度+缩放大小给 hover 物体
        hoverMesh.geometry.copy(target.geometry);
        hoverMesh.position.copy(target.position);
        hoverMesh.rotation.copy(target.rotation);
        hoverMesh.scale.copy(target.scale);
        hoverMesh.visible = true;
      });

      // 办公楼绑定点击事件（注意里面是按下+抬起+判断坐标点来确认你是完整的点击动作）
      MouseHandler.getInstance().addClickMesh(obj3d, async (target) => {
        // 把 2D 物体位移到点击的建筑物位置中心上
        // three.js 会把 3D 物体的设备坐标，转换成屏幕坐标使用 css 的位移来移动你的原生标签
        buildDiv2d.position.copy(target.position);
        buildDiv2d.visible = true;

        // 思路：点击建筑楼宇拿到名字, 去数据对象里换取摄像机坐标位置，设置给摄像机
        const targetP = CameraBuildPositon[target.name];
        cameraMove(camera, controls, targetP, target.position);
        controls.enabled = false;

        // 开始请求点击的这个楼的详细信息
        const res = await getBuildInfoAPI(target.userData.uid);
        console.log(res);
        store.commit("build/setBuildInfo", res.data);
      });
    } else if (/^地面$/.test(obj3d.name)) {
      // 地面物体 => 隐藏建筑物上的 hover 物体效果
      MouseHandler.getInstance().addHoverMesh(obj3d, () => {
        hoverMesh.visible = false;
      });
    }
  });

  // 目标：等待 3 秒钟，新建一辆入场的汽车
  // setTimeout(async () => {
  //   // 新车的数据（将来是后台给我的，这里我先根据字段，自己编一辆先做效果）
  //   const newCarInfoObj = {
  //     areaId: 4,
  //     areaName: "停车场1号",
  //     car: {
  //       carNumber: "宁B86H67",
  //       carTypeName: "小型车",
  //       chargeType: "temp",
  //       chargeTypeName: "临时停车",
  //       driverName: "党耗罩",
  //       entranceTime: "2023-12-09 22:57:15",
  //       modelIndex: 6,
  //       parkingTime: "1分钟",
  //       status: 1, // status 2: 已进场，1：待入场，0 待出场
  //     },
  //     parkNum: "停车位2号",
  //     parkNumber: 2,
  //   };
  //   const carModel = modelList[newCarInfoObj.car.modelIndex].clone();
  //   const car = new Car(
  //     scene,
  //     camera,
  //     controls,
  //     carModel,
  //     newCarInfoObj,
  //     carInfo2d
  //   );
  //   carList.push(car);
  //   // 车辆准备入场 => 运动到一体杆前
  //   await car.moveEnterFormStartToPole();
  //   // 抬杆
  //   await liftingRod(scene, newCarInfoObj, true);
  //   // 车辆移动到车位
  //   await car.movePoleToParkingSpace();
  //   // 落杆
  //   await liftingRod(scene, newCarInfoObj, false);
  // }, 3000);

  // // 目标：等待 3 秒钟，模拟一辆出场的汽车
  // setTimeout(async () => {
  //   // 1. 拿到要出场汽车的数据
  //   const newCarInfoObj = {
  //     areaId: 4,
  //     areaName: "停车场1号", // 停车场区域名字
  //     car: {
  //       carNumber: "赣H11J9F",
  //       carTypeName: "小型车",
  //       chargeType: "temp",
  //       chargeTypeName: "临时停车",
  //       driverName: "莫苛",
  //       entranceTime: "2023-12-09 15:56:11",
  //       modelIndex: 3, // 模型下标
  //       parkingTime: "48分钟",
  //       status: 0, // 2 已在场，1 待进场，0 待出场
  //     },
  //     id: 31, // 车辆 id
  //     parkNum: "停车位1号", // 车辆所占车位名
  //     parkNumber: 1, // 车辆所占车位数字
  //   };
  //   // 2. 根据数据找到 carList 现有这辆车实例对象
  //   const targetCar = carList.find(
  //     (carObj) => carObj.carDataObj.id === newCarInfoObj.id
  //   );
  //   // 3. 让汽车模型开到出场一体杆前
  //   await targetCar.moveParkingSpaceToPole();
  //   // 4. 抬杆 (参数2 车辆数据对象 status 需要为 0，才能准确拿到出场一体杆物体)
  //   await liftingRod(scene, newCarInfoObj, true);
  //   // 5. 出场移动
  //   await targetCar.movePoleToLeavePark();
  //   // 6. 把车辆模型删除，数据从 carList 里移除
  //   scene.remove(targetCar.carModel);
  //   const ind = carList.findIndex(
  //     (carObj) => carObj.carDataObj.id === newCarInfoObj.id
  //   );
  //   carList.splice(ind, 1);
  //   // 7. 落杆
  //   liftingRod(scene, newCarInfoObj, false);
  // }, 3000);

  // 链接服务器-获取实际场景数据，到 3D 场景中模拟效果
  // 初始化车场里汽车
  let nowCar = null; // 现在正在做效果的车辆
  function connect(reset) {
    // 因为现在大家都用的 demo 用户（数据共享）
    // 车辆每个人想要不同的车辆数据（管理进场出场）-> 每个人传递不同的模型 id（后台会根据不同模型 id 返回不同车辆数据）
    const modelUuid = "38a22f2a-1331-525a-8b1d-9c1a92749d52";
    let url = `wss://api-hmzs.itheima.net/tj/websocket/${modelUuid}?accessToken=${getCookie()}`;
    // 如果本次是重新连接，拼接参数-后台不会再返回新列表
    reset && (url += "&reset=true");
    const socket = new WebSocket(url);
    socket.onopen = () => {
      console.log("WebSocket 连接已建立");
    };
    socket.onmessage = (event) => {
      const data = JSON.parse(event.data);
      console.log("WebSocket 收到消息：", data);
      // 经验：socket 接口会一个字段，标记本次数据对应的业务类型
      if (data.type === "PARKING_LIST") {
        // 初始化车场里的汽车
        initParkListFn(data.msg.list);
      } else if (data.type === "ENTRANCE") {
        // 来车了, 先到一体杆前停下 - 用辅助工具模拟摄像探头，录入车牌号到后台内
        initEnterCar(data.msg.data);
      } else if (data.type === "ENTRANCE_SUCCESS_REMIND") {
        // 车辆录入成功（准备抬杆入场到车位）
        enterParkNum(socket, data);
      } else if (data.type === "EXIT") {
        // 车辆要从车场里离开
        leaveCar(data.msg.data);
      } else if (data.type === "EXIT_SUCCESS_REMIND") {
        // 辅助工具已经设置车辆出场成功，我们要继续做抬杆->开车->销毁模型
        leaveCarFinish(socket, data);
      }
    };
    socket.onclose = (event) => {
      console.log("WebSocket 连接已关闭：", event);
      if (event.code === 1006) {
        // 意外断开，重新连接
        connect(true)
      }
    };
  }
  function initParkListFn(list) {
    list.forEach((dataObj) => {
      // 问题：如果多个车用同一个车辆模型对象，会共同一个
      // 解决：物体是可以调用 clone 方法克隆一个完全的一致的物体对象出来
      const car = new Car(
        scene,
        camera,
        controls,
        carModelList[dataObj.car.modelIndex].clone(),
        dataObj,
        carInfo2d
      );

      carList.push(car);
    });
  }
  async function initEnterCar(newCarInfoObj) {
    const carModel = carModelList[newCarInfoObj.car.modelIndex].clone();
    const car = new Car(
      scene,
      camera,
      controls,
      carModel,
      newCarInfoObj,
      carInfo2d
    );
    carList.push(car);
    nowCar = car;
    // 车辆准备入场 => 运动到一体杆前
    await car.moveEnterFormStartToPole();
    // // 抬杆
    // await liftingRod(scene, newCarInfoObj, true);
    // // 车辆移动到车位
    // await car.movePoleToParkingSpace();
    // // 落杆
    // await liftingRod(scene, newCarInfoObj, false);
  }
  async function enterParkNum(socket, data) {
    // 抬杆
    await liftingRod(scene, nowCar.carDataObj, true);
    // 车辆移动到车位
    await nowCar.movePoleToParkingSpace();
    // 落杆
    await liftingRod(scene, nowCar.carDataObj, false);
    nowCar.carDataObj.car.status = 2;
    setTimeout(() => {
      // 给后端 socket 接口发送消息（告诉此辆车动画已经做完了）- 后端等待 5 秒后再模拟一个新的数据给我
      // 现实项目中，是不需要这个动作的
      socket.send(
        JSON.stringify({
          type: "FINISH",
          id: data.msg.data.id,
          status: 1, // 进场
        })
      );
    }, 1000);
  }
  async function leaveCar(leaveCarDataObj) {
    // 1. 通过要离开的车辆数据，找到我已经在 carList 保存过当前车场里对应车实例对象
    const car = carList.find((carObj) => {
      return carObj.carDataObj.id === leaveCarDataObj.id;
    });
    // 车辆状态修改
    car.carDataObj.car.status = 0;
    // 2. 调用它的离场方法
    car.moveParkingSpaceToPole();
    nowCar = car;
  }
  async function leaveCarFinish(socket, data) {
    // 抬杆 (参数2 车辆数据对象 status 需要为 0，才能准确拿到出场一体杆物体)
    await liftingRod(scene, nowCar.carDataObj, true);
    // 出场移动
    await nowCar.movePoleToLeavePark();
    // 把车辆模型删除，数据从 carList 里移除
    scene.remove(nowCar.carModel);
    const ind = carList.findIndex(
      (carObj) => carObj.carDataObj.id === nowCar.carDataObj.id
    );
    carList.splice(ind, 1);
    // 7. 落杆
    liftingRod(scene, nowCar.carDataObj, false);
    setTimeout(() => {
      socket.send(
        JSON.stringify({
          type: "FINISH",
          id: data.msg.data.id,
          status: 0, // 出场
        })
      );
    }, 1000);
  }
  // 流程：第一次 socket 链接，后台返回默认车场里现有车辆数据列表（展示），等待一定时间，后台会返回模拟的进场/出场消息对应数据，前台把数据用 3D 模型效果实现对应进场/出场流程
  // 1. 进场/出场，先把车辆开到一体杆面前
  // 2. 去辅助工具网站（此网站模拟监控摄像探头）录入进场/出场的车牌信息（注意辅助网站的模型 id 和代码里你用的一致，保证操作数据是你的）
  // 3. 辅助工具网站录入后，后台再次发送消息给前端，前端收到消息，抬杆，移动车辆到车位/出场删除，落杆
  // 4. socket 给后端发个消息，让后端再次产生一份模拟数据给我，再从头执行一遍流程

  connect();

  // 渲染循环
  function renderLoop() {
    renderer.render(scene, camera);
    // 3. 在它的 div 盒子上进行渲染改变位置（进行 translate 等位移用）
    css2dRenderer.render(scene, camera);
    controls.update();
    EffectManager.getInstance().update();
    requestAnimationFrame(renderLoop);
  }
  renderLoop();
});

// 技巧：找摄像机最佳的起始位置
// 给轨道控制器绑定 change 事件并打印摄像机当前位置和观察的坐标点
// controls.addEventListener('change', () => {
//   console.log(camera.position) // 摄像机坐标点
//   console.log(controls.target) // 轨道控制器影响摄像机观察的坐标点
// })
</script>

<style lang='scss' scoped>
.all-charts {
  position: absolute;
  top: 0;
  left: 0;
  width: 480px;
  height: 100%;
  padding: 20px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  background: linear-gradient(
    to left,
    rgba(0, 6, 15, 0) 0%,
    rgba(0, 6, 15, 0) 20%,
    rgba(0, 0, 0, 0.4) 30%,
    rgba(0, 0, 0, 0.6) 40%,
    rgba(1, 4, 11, 1) 70%,
    #04070d 100%
  );

  .img-header {
    height: 30px;
  }
}

.section-one {
  flex-basis: 25%;
  .icons-container {
    display: flex;
    justify-content: space-between;
    padding: 20px 0;

    .item {
      text-align: center;
      display: flex;
      flex-direction: column;
      flex: 1;

      .icons-item {
        height: 80px;
        position: relative;

        .number {
          position: absolute;
          left: 50%;
          transform: translateX(-50%);
          font-size: 18px;
          font-family: FontquanXinYiGuanHeiTi, FontquanXinYiGuanHeiTi-Regular;
          color: #ffffff;
        }
      }

      .building-icon {
        background: url("@/assets/building-icon.png") no-repeat 50% 0 / contain;
      }

      .enterprise-icon {
        background: url("@/assets/enterprise-icon.png") no-repeat 50% 0 /
          contain;
      }

      .rod-icon {
        background: url("@/assets/rod-icon.png") no-repeat 50% 0 / contain;
      }

      .car-icon {
        background: url("@/assets/car-icon.png") no-repeat 50% 0 / contain;
      }

      .title,
      .unity {
        font-size: 14px;
        color: #cdd7e1;
      }

      .title {
        margin-top: 8px;
      }
    }
  }
}

.section-two {
  // 主轴宽度（y轴）
  flex-basis: 35%;
  margin-top: 50px;

  .bar-chart {
    width: 100%;
    height: calc(100% - 90px);
  }
}

.section-three {
  flex-basis: 40%;
  margin-top: 50px;
  .pie-chart {
    position: relative;
    margin: 0 auto;
    padding-bottom: 20px;
    width: 80%;
    height: calc(100% - 40px);
  }
}

.model-container,
.canvas-3d {
  height: 100%;
}
</style>